#include <WiFi.h>
#include <WiFiUdp.h>
#include <NetTime.h>
#include <Hardware.h>

#define LOCAL_PORT 8888    // local port to listen for UDP packets
#define NTP_PACKET_SIZE 48 // NTP time stamp is in the first 48 bytes of the message
const char *ntpServers[] = {"time1.cloud.tencent.com", "cn.pool.ntp.org", "ntp1.aliyun.com"};

time_t NetTime::getNetTime()
{
    struct timeval tv;

    tv.tv_sec = 0;    
    FILE *fp = fopen("/sd/wifi.cfg", "r");
    if(fp)
    {
        char *wifiSSID = new char[64];
        char *wifiPWD = new char[32];

        // 读入网络设置
        fgets(wifiSSID, 64, fp);
        wifiSSID[strlen(wifiSSID)-2] = 0;
        fgets(wifiPWD, 32, fp);
        wifiPWD[strlen(wifiPWD)-2] = 0;
        if (connectWiFi(wifiSSID, wifiPWD))
        {
            // 同步时间
            for (int i = 0; i < 3; i++)
            {
                struct timeval tv;
                tv.tv_sec = getNTPtime(i);
                if(tv.tv_sec)
                {
                    break;
                }
            }
            WiFi.disconnect();
        }

        delete wifiPWD;
        delete wifiSSID;

        fclose(fp);
    }

    return tv.tv_sec;
}

bool NetTime::connectWiFi(char *pWiFiSSID, char *pWiFiPWD)
{
    WiFi.mode(WIFI_STA);
    
    dbLog("\nconnectWiFi");
    dbLog("SSID: ");
    dbLog(pWiFiSSID);
    dbLog(" PWD: ");
    dbLog(pWiFiPWD);
    WiFi.begin(pWiFiSSID, pWiFiPWD);

    for (int tryTimes = 0; tryTimes < 12; tryTimes++)
    {
        dbLog(".");
        if (WiFi.status() == WL_CONNECTED)
        {
            dbLog("\nConnect!");
            return true;
        }
        delay(500);
    }
    dbLog("\nNot connected!");

    return false;
}

uint32_t NetTime::getNTPtime(int index)
{
    WiFiUDP Udp;
    byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
    
    dbLog("\ngetNTPtime");
    Udp.begin(LOCAL_PORT);
    // set all bytes in the buffer to 0
    memset(packetBuffer, 0, NTP_PACKET_SIZE);
    // Initialize values needed to form NTP request
    // (see URL above for details on the packets)
    packetBuffer[0] = 0b11100011; // LI, Version, Mode
    packetBuffer[1] = 0;          // Stratum, or type of clock
    packetBuffer[2] = 6;          // Polling Interval
    packetBuffer[3] = 0xEC;       // Peer Clock Precision
    // 8 bytes of zero for Root Delay & Root Dispersion
    packetBuffer[12] = 49;
    packetBuffer[13] = 0x4E;
    packetBuffer[14] = 49;
    packetBuffer[15] = 52;

    // all NTP fields have been given values, now
    // you can send a packet requesting a timestamp:
    dbLog("\nSend NTP packet.");
    Udp.beginPacket(ntpServers[index], 123); //NTP requests are to port 123
    Udp.write(packetBuffer, NTP_PACKET_SIZE);
    Udp.endPacket();

    // wait to see if a reply is available
    // 最多等1.5秒
    unsigned long timeOut = millis() + 1500;
    dbLog("\nWait reply.");
    while (Udp.available() < NTP_PACKET_SIZE && millis() < timeOut)
    {
        delay(10);
    }

    dbLog("\nParse packet.");
    if (Udp.parsePacket())
    {
        // We've received a packet, read the data from it
        Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer

        // the timestamp starts at byte 40 of the received packet and is four bytes,
        // or two words, long. First, extract the two words:

        unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
        unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
        // combine the four bytes (two words) into a long integer
        // this is NTP time (seconds since Jan 1 1900):
        unsigned long secsSince1900 = highWord << 16 | lowWord;

        // now convert NTP time into everyday time:
        // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
        const unsigned long seventyYears = 2208988800UL;
        // subtract seventy years:
        uint32_t epoch = secsSince1900 - seventyYears;

        // 同步到系统时间
        // 后续直接用 time(NULL)可直接得到系统时间
        // time_t t = time(NULL);
        // tm m = gmtime(t);

        dbLog("\nNTP time ok.");
        return epoch + 8 * 3600;// 北京时区是东8区
    }
    dbLog("\nNTP time failed!.");
    return 0;
}